﻿using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using mozhi.smarterp.AuthorCenter.ApiMetadatas.Repositories;
using mozhi.smarterp.AuthorCenter.Enums;
using mozhi.smarterp.AuthorCenter.OpenIddict.Repositories;
using mozhi.smarterp.AuthorCenter.Permissions.Repositories;
using mozhi.smarterp.AuthorCenter.Users.Permissions;
using mozhi.smarterp.AuthorCenter.Users.Repositories;
using mozhi.smarterp.AuthorCenter.Users.Services;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Clients;
using Volo.Abp.Data;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Identity;
using Volo.Abp.ObjectExtending;
using Volo.Abp.PermissionManagement;
using Volo.Abp.Users;

namespace mozhi.smarterp.AuthorCenter.Users
{
    [Dependency(ReplaceServices = true)]
    [ExposeServices(typeof(IIdentityUserAppService), typeof(IdentityUserAppService), typeof(ICustomIdentityUserAppService), typeof(CustomIdentityUserAppService))]
    public class CustomIdentityUserAppService : IdentityUserAppService, ICustomIdentityUserAppService
    {
        private ICurrentClient _currentClient;
        private IOpenIddictClientTypeRepository _openIddictUserTypeRepository;
        private ICustomIdentityUserRepository _userRepository;
        private CustomIdentityUserManager _userManager;
        private IUserDataPermissionRepository _userDataPermissionRepository;
        private ICurrentUser _currentUser;
        private IPermissionGrantRepository _permissionGrantRepository;
        private IPermissionGroupRepository _permissionGroupRepository;
        private IPermissionItemApiGrantRepository _permissionItemApiGrantRepository;
        private IApiMetadataRepository _apiMetadataRepository;

        public CustomIdentityUserAppService(CustomIdentityUserManager userManager, ICustomIdentityUserRepository userRepository, IIdentityRoleRepository roleRepository, IOptions<IdentityOptions> identityOptions , 
            ICurrentClient currentClient, IOpenIddictClientTypeRepository openIddictUserTypeRepository , IUserDataPermissionRepository userDataPermissionRepository , ICurrentUser currentUser ,
            IPermissionGrantRepository permissionGrantRepository , IPermissionGroupRepository permissionGroupRepository , IPermissionItemApiGrantRepository permissionItemApiGrantRepository , IApiMetadataRepository apiMetadataRepository) : 
            base(userManager, userRepository, roleRepository, identityOptions)
        {
            _currentClient = currentClient;
            _openIddictUserTypeRepository = openIddictUserTypeRepository;
            _userRepository = userRepository;
            _userManager = userManager;
            _userDataPermissionRepository = userDataPermissionRepository;
            _currentUser = currentUser;
            _permissionGrantRepository = permissionGrantRepository;
            _permissionGroupRepository = permissionGroupRepository;
            _permissionItemApiGrantRepository = permissionItemApiGrantRepository;
            _apiMetadataRepository = apiMetadataRepository;
        }

        [AllowAnonymous]
        public override async Task<IdentityUserDto> CreateAsync(IdentityUserCreateDto input)
        {
            await IdentityOptions.SetAsync().ConfigureAwait(continueOnCapturedContext: false);
            IdentityUser user = new IdentityUser(base.GuidGenerator.Create(), input.UserName, input.Email, base.CurrentTenant.Id);
            input.MapExtraPropertiesTo(user);

            var clientId = _currentClient.Id;

            var clientType = (await _openIddictUserTypeRepository.GetListAsync(null, int.MaxValue, 0, clientId)).FirstOrDefault();

            var userTypes = GetUserTypesByClientType(clientType?.ClientType);

            if (!userTypes.Contains((UserType)input.GetProperty<int>("UserType")))
                throw new UserFriendlyException($"不能创建类型为：{input.GetProperty<int>("UserType")}的用户");

            (await UserManager.CreateAsync(user, input.Password).ConfigureAwait(continueOnCapturedContext: false)).CheckErrors();
            await UpdateUserByInput(user, input).ConfigureAwait(continueOnCapturedContext: false);
            (await UserManager.UpdateAsync(user).ConfigureAwait(continueOnCapturedContext: false)).CheckErrors();
            await base.CurrentUnitOfWork.SaveChangesAsync().ConfigureAwait(continueOnCapturedContext: false);
            return base.ObjectMapper.Map<IdentityUser, IdentityUserDto>(user);
        }

        [AllowAnonymous]
        public override async Task<IdentityUserDto> UpdateAsync(Guid id, IdentityUserUpdateDto input)
        {
            var clientId = _currentClient.Id;

            var clientType = (await _openIddictUserTypeRepository.GetListAsync(null, int.MaxValue, 0, clientId)).FirstOrDefault();

            var userTypes = GetUserTypesByClientType(clientType?.ClientType);

            if (!userTypes.Contains((UserType)input.GetProperty<int>("UserType")))
                throw new UserFriendlyException($"不能更改用户类型为：{input.GetProperty<int>("UserType")}");

            await IdentityOptions.SetAsync().ConfigureAwait(continueOnCapturedContext: false);
            IdentityUser user = await UserManager.GetByIdAsync(id).ConfigureAwait(continueOnCapturedContext: false);
            user.SetConcurrencyStampIfNotNull(input.ConcurrencyStamp);
            (await UserManager.SetUserNameAsync(user, input.UserName).ConfigureAwait(continueOnCapturedContext: false)).CheckErrors();
            await UpdateUserByInput(user, input).ConfigureAwait(continueOnCapturedContext: false);
            input.MapExtraPropertiesTo(user);
            (await UserManager.UpdateAsync(user).ConfigureAwait(continueOnCapturedContext: false)).CheckErrors();
            if (!input.Password.IsNullOrEmpty())
            {
                (await UserManager.RemovePasswordAsync(user).ConfigureAwait(continueOnCapturedContext: false)).CheckErrors();
                (await UserManager.AddPasswordAsync(user, input.Password).ConfigureAwait(continueOnCapturedContext: false)).CheckErrors();
            }

            await base.CurrentUnitOfWork.SaveChangesAsync().ConfigureAwait(continueOnCapturedContext: false);
            return base.ObjectMapper.Map<IdentityUser, IdentityUserDto>(user);
        }

        public async Task<PagedResultDto<IdentityUserDto>> GetListAsync(IdentityUserFilter input)
        {
            var clientId = _currentClient.Id;

            var clientType = (await _openIddictUserTypeRepository.GetListAsync(null, int.MaxValue, 0, clientId)).FirstOrDefault();

            var userTypes = GetUserTypesByClientType(clientType?.ClientType);

            if (userTypes.Count == 0)
                return new PagedResultDto<IdentityUserDto>();

            if (input.UserType.HasValue && !userTypes.Contains((UserType)input.UserType.Value))
                return new PagedResultDto<IdentityUserDto>();

            if (input.UserType.HasValue)
                userTypes = new List<UserType>() { (UserType)input.UserType.Value };

            long count = await _userRepository.GetCountAsync(input.Filter, userTypes).ConfigureAwait(continueOnCapturedContext: false);
            List<IdentityUser> source = await _userRepository.GetListAsync(input.Sorting, input.MaxResultCount, input.SkipCount, input.Filter ,false , userTypes).ConfigureAwait(continueOnCapturedContext: false);

            var dtos = ObjectMapper.Map<List<IdentityUser>, List<IdentityUserDto>>(source);

            await LoadUserRoleNameAysnc(dtos);

            return new PagedResultDto<IdentityUserDto>(count, dtos);
        }

        [AllowAnonymous]
        public override Task<IdentityUserDto> GetAsync(Guid id)
        {
            return base.GetAsync(id);
        }

        [AllowAnonymous]
        public async override Task<ListResultDto<IdentityRoleDto>> GetRolesAsync(Guid id)
        {
            return await base.GetRolesAsync(id);
        }

        public async Task<UserDataPermissionDto> GetDataPermissionAsync(Guid userId)
        {
            var dataPermission = await _userDataPermissionRepository.FindAsync(userId);

            return ObjectMapper.Map<UserDataPermission, UserDataPermissionDto>(dataPermission);
        }


        public async Task<List<UserAuthorizedUrlDto>> GetUserAuthorizedUrlsAsync()
        {
            var result = new List<UserAuthorizedUrlDto>();

            var userId = _currentUser?.Id;

            if (userId == null)
                return result;

            var userRoles = await _userRepository.GetRolesAsync(userId.Value);

            if (userRoles.Count == 0)
                return result;

            var rolePermissions = await _permissionGrantRepository.GetListAsync("R", userRoles.First().Name);

            if (rolePermissions.Count == 0)
                return result;

            var permissionItemNames = rolePermissions.Select(x => x.Name).Distinct().ToList();

            var permissionItems = await _permissionGroupRepository.GetItemListAsync(permissionItemNames);
            
            if(permissionItems.Count==0)
                return result;

            var permissionItemIds = permissionItems.Select(x=>x.Id).Distinct().ToList();

            var apiGrants = await _permissionItemApiGrantRepository.GetListAsync(permissionItemIds);

            var apiMetadataIds = apiGrants.Select(x=>x.ApiMetadataId).Distinct().ToList();

            var apiMetadatas = await _apiMetadataRepository.GetListAsync(apiMetadataIds);

            if (apiMetadatas.Count == 0)
                return result;

            foreach (var apiMetadata in apiMetadatas)
            {
                var userAuthorizedUrl = new UserAuthorizedUrlDto() { Path = apiMetadata.Path, Method = apiMetadata.Method };
                
                result.Add(userAuthorizedUrl);
            }

            return result;
        }

        #region
        private List<UserType> GetUserTypesByClientType(ClientType? clientType)
        {
            if(clientType== ClientType.PlatformClient)
                return new List<UserType>() { UserType.PlatformAdmin , UserType.EnterpriseAdmin};

            if (clientType == ClientType.EnterpriseClient)
                return new List<UserType>() { UserType.EnterpriseAdmin, UserType.Normal };

            if(clientType== ClientType.Normal)
                return new List<UserType> { UserType.Normal };

            return new List<UserType>();
        }


        private async Task LoadUserRoleNameAysnc(List<IdentityUserDto> users)
        {
            foreach(var user in users)
            {
                var roles = await base.GetRolesAsync(user.Id);

                var roleNames = roles.Items?.Select(x=>x.Name).ToArray();

                user.ExtraProperties["RoleNames"] = roleNames; 
            }
        }

        protected override async Task UpdateUserByInput(IdentityUser user, IdentityUserCreateOrUpdateDtoBase input)
        {
            await base.UpdateUserByInput(user, input);

            if (input.ExtraProperties.ContainsKey("DataPermission") && input.ExtraProperties["DataPermission"]!=null)
            {
                var value = input.ExtraProperties["DataPermission"].ToString();

                if (!value.IsNullOrEmpty())
                {
                    var dataPermissionInput = JsonConvert.DeserializeObject<UserDataPermissionUpdateDto>(value);

                    if (dataPermissionInput != null)
                        await _userManager.UpdateUserDataPermissionAsync(user, dataPermissionInput.IsOwnDepartment, dataPermissionInput.IsOwnCreated);
                }

               
            }

            
        }

        #endregion
    }
}
